﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.Media.ISO
{
    internal class BoxBinaryWriter : BinaryWriter
    {
        static readonly DateTime offsetDate = new DateTime(1904, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        /// <summary>
        /// Initializes a new instance of the <see cref="BoxBinaryWriter"/> class.
        /// </summary>
        /// <param name="outputStream">The output stream.</param>
        public BoxBinaryWriter(Stream outputStream)
            : base(outputStream, Encoding.UTF8)
        { }

#if NETFX_CORE
        /// <summary>
        /// Initializes a new instance of the <see cref="BoxBinaryWriter"/> class.
        /// </summary>
        /// <param name="outputStream">The output stream.</param>
        /// <param name="leaveOpen">true to leave the stream open after the BoxBinaryWriter object is disposed; otherwise, false.</param>
        public BoxBinaryWriter(Stream outputStream, bool leaveOpen)
            : base(outputStream, Encoding.UTF8, leaveOpen)
        { }
#endif

        public void WriteBytes(byte b, uint count)
        {
            for (int i = 0; i < count; i++)
            {
                this.Write(b);
            }
        }

        public override void Write(string value)
        {
            this.Write(value.ToCharArray());
        }

        public override void Write(ushort value)
        {
            base.Write(BitConverter.GetBytes(value).Reverse().ToArray());
        }

        public override void Write(uint value)
        {
            base.Write(BitConverter.GetBytes(value).Reverse().ToArray());
        }

        public override void Write(ulong value)
        {
            base.Write(BitConverter.GetBytes(value).Reverse().ToArray());
        }

        public void WriteUInt24(uint value)
        {
            base.Write(BitConverter.GetBytes(value).Reverse().Skip(1).ToArray());
        }

        public void WriteIntValueInBytes(uint value, byte numberOfBytes)
        {
            switch (numberOfBytes)
            {
                case 0:
                    Write((byte)value);
                    break;
                case 1:
                    Write((ushort)value);
                    break;
                case 2:
                    WriteUInt24(value);
                    break;
                case 3:
                    Write(value);
                    break;
                default:
                    throw new ArgumentOutOfRangeException("numberOfBytes");
            }
        }

        public void WriteNullTerminatedString(string value)
        {
            this.Write(value);
            base.Write((byte)0); // null terminate the string
        }

        public void Write(Guid s)
        {
            WriteHex(s.ToString("N").ToUpper());
        }

        public void Write(DateTimeOffset d)
        {
            this.Write((ulong)d.Subtract(offsetDate).TotalSeconds);
        }

        public void WriteUInt32(DateTimeOffset d)
        {
            this.Write((uint)d.Subtract(offsetDate).TotalSeconds);
        }

        public void WriteHex(string s)
        {
            for (int i = 0; i < s.Length; i = i + 2)
            {
                base.Write(Convert.ToByte(s.Substring(i, 2), 16));
            }
        }

        public void WriteToUSort(string s)
        {
            if (s.Length != 3) throw new ArgumentOutOfRangeException("s", "string needs to be 3 characters long");

            ushort result = 0;
            for (int i = 0; i < 3; i++)
            {
                var ci = (int)s[i];
                ci -= 0x60;
                result += (ushort)(ci << (2 - i) * 5);
            }

            Write(result);
        }
    }
}
